﻿using System.IO;
using System.Text;
using System.Security.Cryptography;

using System.Reflection;

namespace SampleApp
{
    /// <summary>
    /// The encryption namespace isn't available for PCLs.  In this case, create your own Encryptor
    /// class using the IEncryptor interface and set it into the FileDb object via SetEncryptor.
    /// 
    /// This is the same implementation used in the FileDb DLL (Windows platform only)
    /// </summary>
    /// 
    public class MyEncryptor : FileDbNs.IEncryptor
    {
        byte[] _key;
        AesManaged _encryptor;

        /// <summary>
        /// Constructor taking a key (password) and salt as a string
        /// </summary>
        /// <param name="encryptionKey"></param>
        /// <param name="salt"></param>
        /// 
        public MyEncryptor( string encryptionKey, string salt )
        {
            _key = GetHashKey( encryptionKey, salt );
            _encryptor = new AesManaged();

            // Set the key
            _encryptor.Key = _key;
            _encryptor.IV = _key;
        }

        private static byte[] GetHashKey( string hashKey, string salt )
        {
            // Initialise
            UTF8Encoding encoder = new UTF8Encoding();

            // Get the salt
            byte[] saltBytes = encoder.GetBytes( salt );

            // Setup the hasher
            Rfc2898DeriveBytes rfc = new Rfc2898DeriveBytes( hashKey, saltBytes );

            // Return the key
            return rfc.GetBytes( 16 );
        }

        /// <summary>
        /// Encrypt the passed byte array
        /// </summary>
        /// <param name="dataToEncrypt">The data to encrypt</param>
        /// <returns>The encrypted data</returns>
        /// 
        public byte[] Encrypt( byte[] dataToEncrypt )
        {
            byte[] bytes = null;
            MemoryStream outStrm = new MemoryStream( (int) (dataToEncrypt.Length * 1.5) );

            // Create the crypto stream
            using( CryptoStream encrypt = new CryptoStream( outStrm, _encryptor.CreateEncryptor(), CryptoStreamMode.Write ) )
            {
                // Encrypt
                encrypt.Write( dataToEncrypt, 0, dataToEncrypt.Length );
                encrypt.FlushFinalBlock();
                bytes = outStrm.ToArray();
                encrypt.Close();
            }
            return bytes;
        }

        /// <summary>
        /// Decrypt the passed byte array
        /// </summary>
        /// <param name="encryptedData">The data to decrypt</param>
        /// <returns>The decrypted data</returns>
        /// 
        public byte[] Decrypt( byte[] encryptedData )
        {
            byte[] bytes = null;
            MemoryStream outStrm = new MemoryStream( (int) (encryptedData.Length * 1.5) );

            // Create the crypto stream
            using( CryptoStream decrypt = new CryptoStream( outStrm, _encryptor.CreateDecryptor(), CryptoStreamMode.Write ) )
            {
                // Encrypt
                decrypt.Write( encryptedData, 0, encryptedData.Length );
                decrypt.FlushFinalBlock();
                bytes = outStrm.ToArray();
                decrypt.Close();
            }
            return bytes;
        }
    }
}
